package com.imu.genia;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author genia
 * @Date 2022/5/13
 **/
public class IClassPathXmlApplicationContext implements IApplicationContext {
    //定义beanContainer保存bean,key-value
    private Map<String, Object> beanContainer = new HashMap<String, Object>();


    public IClassPathXmlApplicationContext(String path) {
        //将spring.xml转成Document对象
        SAXReader reader = new SAXReader();
        try {

            URL url = IApplicationContext.class.getClassLoader().getResource(path);
            Document document = reader.read(url);
            //dom4j 解析 XML
            //获取根节点对象<beans>
            Element rootElement = document.getRootElement();
            //迭代取子节点对象<bean>，获取一个迭代器
            List<Element> beans = rootElement.elements("bean");

            for (Element b : beans) {
                //取key，bean中的id属性值
                String key = b.attributeValue("id");
                //取类名，bean中的class属性值
                String value = b.attributeValue("class");
                if(Objects.nonNull(value)) {
                    beanContainer.put(key, Class.forName(value).newInstance());
                }
            }
            //依赖注入
            for (Element b : beans) {
                if (b.elements("constructor-arg").size() > 0){
                    //取constructor-arg标签
                    List<Element> properties = b.elements("constructor-arg");
                    for (Element p : properties) {
                        //获取property中name、value、ref、id的值
                        String pName = p.attributeValue("name");
                        String pRef = p.attributeValue("ref");
                        Object refObject = beanContainer.get(pRef);
                        String key = b.attributeValue("id");
                        //取对象
                        Object object = beanContainer.get(key);
                        //取字节码信息
                        Class c = object.getClass();
                        //反射，构造函数
                        Constructor constructor = c.getConstructor(refObject.getClass());
                        //重新装配
                        object=constructor.newInstance(refObject);
                        beanContainer.put(key,object);
                    }
                }
                else if (b.elements("property").size() > 0){
                    //获取property标签及name、value、ref、id的值
                    List<Element> properties = b.elements("property");
                    for (Element p : properties) {
                        String pName = p.attributeValue("name");
                        String pBean = p.element("ref").attributeValue("bean");
                        Object refObject = beanContainer.get(pBean);
                        String key = b.attributeValue("id");
                        //取对象
                        Object object = beanContainer.get(key);
                        //取字节码信息
                        Class c = object.getClass();
                        //set
                        final Method setMethod = c.getMethod("set" + pName.substring(0,1).toUpperCase() + pName.substring(1),refObject.getClass());
                        setMethod.invoke(object,refObject);
                        beanContainer.put(key,object);
                    }
                }
                else if (Objects.nonNull(b.attributeValue("factory-bean"))){
                    //取property标签
                    String factoryBean = b.attributeValue("factory-bean");
                    String factoryMethod = b.attributeValue("factory-method");
                    String key = b.attributeValue("id");
                    //取beanFactory
                    Object beanFactory = beanContainer.get(factoryBean);
                    //取工厂对象的方法
                    final Method method = beanFactory.getClass().getMethod(factoryMethod);
                    //创建对象
                    Object object = method.invoke(beanFactory);
                    beanContainer.put(key,object);
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String id) {
        return beanContainer.get(id);
    }
}
